﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Diagnostics;

using PickGold.Attributes;

using HsjCommon = PickGold.Common;

namespace PickGold.Base.Remoting
{
	/// <summary>
	/// 导入当前程序集所有未知类型。
	/// </summary>
	public static class KnownTypeProvider
	{
		/// <summary>
		/// GetKnownTypes
		/// </summary>
		public const string MethodName = "GetKnownTypes";
		/// <summary>
		/// GetXmlKnownTypes
		/// </summary>
		public const string XmlMethodName = "GetXmlKnownTypes";
		/// <summary>
		/// GetSoapKnownTypes
		/// </summary>
		public const string SoapMethodName = "GetSoapKnownTypes";
		/// <summary>
		/// GetBinaryKnownTypes
		/// </summary>
		public const string BinaryMethodName = "GetBinaryKnownTypes";

		//private static Dictionary<ICustomAttributeProvider, Type[]> _Cache;
		private static int _CodeCount;

		#region Default GetKnownTypes

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public static IEnumerable<Type> GetKnownTypes()
		{
			return KnownTypeProvider.GetKnownTypes<DataContractAttribute>(null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetKnownTypes(object knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<DataContractAttribute>(knownTypeAttributeTarget as ICustomAttributeProvider);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<DataContractAttribute>(knownTypeAttributeTarget);
		}

		#endregion

		#region XML GetXmlKnownTypes

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public static IEnumerable<Type> GetXmlKnownTypes()
		{
			return KnownTypeProvider.GetKnownTypes<XmlSerializerFormatAttribute>(null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetXmlKnownTypes(object knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<XmlSerializerFormatAttribute>(knownTypeAttributeTarget as ICustomAttributeProvider);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetXmlKnownTypes(ICustomAttributeProvider knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<XmlSerializerFormatAttribute>(knownTypeAttributeTarget);
		}

		#endregion

		#region Soap GetSoapKnownTypes

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public static IEnumerable<Type> GetSoapKnownTypes()
		{
			return KnownTypeProvider.GetKnownTypes<DataContractAttribute>(null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetSoapKnownTypes(object knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<DataContractAttribute>(knownTypeAttributeTarget as ICustomAttributeProvider);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetSoapKnownTypes(ICustomAttributeProvider knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<DataContractAttribute>(knownTypeAttributeTarget);
		}

		#endregion

		#region Binary GetBinaryKnownTypes

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public static IEnumerable<Type> GetBinaryKnownTypes()
		{
			return KnownTypeProvider.GetKnownTypes<SerializableAttribute>(null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetBinaryKnownTypes(object knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<SerializableAttribute>(knownTypeAttributeTarget as ICustomAttributeProvider);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetBinaryKnownTypes(ICustomAttributeProvider knownTypeAttributeTarget)
		{
			return KnownTypeProvider.GetKnownTypes<SerializableAttribute>(knownTypeAttributeTarget);
		}

		#endregion

		#region GetKnownTypes<ATTRIBUTE>

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public static IEnumerable<Type> GetKnownTypes<ATTRIBUTE>() where ATTRIBUTE : Attribute
		{
			//return typeof(KnownTypeProvider).Assembly.GetTypes();
			return KnownTypeProvider.GetKnownTypes<ATTRIBUTE>(null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetKnownTypes<ATTRIBUTE>(object knownTypeAttributeTarget) where ATTRIBUTE : Attribute
		{
			return KnownTypeProvider.GetKnownTypes<ATTRIBUTE>(knownTypeAttributeTarget as ICustomAttributeProvider);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="knownTypeAttributeTarget"></param>
		/// <returns></returns>
		public static IEnumerable<Type> GetKnownTypes<ATTRIBUTE>(ICustomAttributeProvider knownTypeAttributeTarget) where ATTRIBUTE : Attribute
		{
			var ms = AppDomain.CurrentDomain.GetAssemblies();
			if (HsjCommon.StaticCache.KnownTypes != null && KnownTypeProvider._CodeCount == ms.Length)
				return HsjCommon.StaticCache.KnownTypes;

			var types = new List<Type>();
			lock (KnownTypeProvider.MethodName)
			{
				KnownTypeProvider._CodeCount = ms.Length;
				for (var i = 0; i < KnownTypeProvider._CodeCount; i++)
					types.AddRange(ms[i].GetTypes());
			}

			return KnownTypeProvider.GetKnownTypes<ATTRIBUTE>(types, MethodBase.GetCurrentMethod(), new object[] { knownTypeAttributeTarget });
		}

		private static IEnumerable<Type> GetKnownTypes<ATTRIBUTE>(List<Type> types, MethodBase method, object[] args) where ATTRIBUTE : Attribute
		{
			for (var i = 0; i < types.Count; i++)
			{
				var ts = types[i].GetGenericArguments();
				foreach (var t in ts)
				{
					if (types.Contains(t))
						continue;

					types.Add(t);
				}
			}
			for (var i = types.Count - 1; i >= 0; i--)
			{
				var t = types[i];
				while (t != typeof(object) && t.GetCustomAttributes(typeof(ATTRIBUTE), false).Length > 0)
					t = t.BaseType;
				if(typeof(object) != t)//if (types[i].GetCustomAttributes(typeof(ATTRIBUTE), false).Length == 0)//if (types[i].GetCustomAttributes(typeof(ATTRIBUTE), true).Length == 0)
				{
					types.RemoveAt(i);
					continue;
				}

				t = types[i];
				while (t.HasElementType)
					t = t.GetElementType();
				if (t.IsEnum)
					continue;

				var cs = t.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance);
				foreach (var c in cs)//删除所有未定义无参构造函数的类型。
				{
					if (c.GetParameters().Length == 0)
					{
						t = null;
						cs = null;
						break;
					}
				}
				if (t == null)
					continue;

				types.RemoveAt(i);
			}
			for (var i = 0; i < types.Count; i++)
			{
				if (types[i].IsArray)// || types[i] == t || types[i].IsSubclassOf(t)//var t = typeof(CommunicationException);
					continue;

				if (types[i].IsGenericType)//types[i].IsGenericTypeDefinition
					continue;

				var t = Array.CreateInstance(types[i], 0).GetType();
				types.Add(t);
				t = typeof(FaultException<>);
				t = t.MakeGenericType(types[i]);
				types.Add(t);
				if (t != null)//以下代码永不执行。
					continue;

				#region 排除类别

				if (typeof(NullableBoolean) == types[i])
					continue;

				t = typeof(CDelegateBase);
				if (types[i].IsSubclassOf(t) || types[i] == t)
					continue;

				t = typeof(DelegateAttribute);
				if (types[i].IsSubclassOf(t) || types[i] == t)
					continue;

				#endregion 排除类别

				if (types[i].IsClass)
				{
					t = typeof(EntitySet<>);
					t = t.MakeGenericType(types[i]);
					types.Add(t);
				}
				t = typeof(List<>);
				t = t.MakeGenericType(types[i]);
				types.Add(t);
			}
			for (var i = types.Count - 1; i >= 0; i--)
			{
				if (types[i].IsGenericTypeDefinition)
				{
					if (types[i].GetGenericArguments().Length == 1)
					{
						for (var ii = types.Count - 1; ii >= 0; ii--)
						{
							if (types[ii].IsGenericType || types[ii].IsGenericTypeDefinition)
								continue;

							try
							{
								var t = types[i].MakeGenericType(types[ii]);
								types.Add(t);
							}
							catch { }
						}
					}
					types.RemoveAt(i);
				}
			}

			types.Add(typeof(FaultException));

			//types.Add(typeof(CDelegate));
			//types.Add(typeof(DelegateAttribute));
			//types.Add(typeof(DelegateAttribute[]));

			//if (KnownTypeProvider._Cache == null)
			//	KnownTypeProvider._Cache = new Dictionary<ICustomAttributeProvider, Type[]>();
			//KnownTypeProvider._Cache.Add(knownTypeAttributeTarget, types.ToArray());
			//return KnownTypeProvider._Cache[knownTypeAttributeTarget];

			HsjCommon.StaticCache.KnownTypes = types.ToArray();
			return HsjCommon.StaticCache.KnownTypes;
		}

		#endregion
	}
}
